using System;
using System.Threading.RateLimiting;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;

namespace CKY.AgentPlatform.RateLimiting
{
    /// <summary>
    /// API限流中间件
    /// </summary>
    public class RateLimitingMiddleware : IMiddleware, ITransientDependency
    {
        private readonly DistributedCacheService _cacheService;
        private readonly RateLimitOptions _options;
        private readonly ILogger<RateLimitingMiddleware> _logger;
        private readonly FixedWindowRateLimiter _limiter;

        public RateLimitingMiddleware(
            DistributedCacheService cacheService,
            IOptions<RateLimitOptions> options,
            ILogger<RateLimitingMiddleware> logger)
        {
            _cacheService = cacheService;
            _options = options.Value;
            _logger = logger;

            // 创建固定窗口限流器
            _limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions
            {
                Window = TimeSpan.FromMinutes(_options.WindowMinutes),
                PermitLimit = _options.MaxRequests,
                QueueLimit = _options.QueueLimit
            });
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            // 获取客户端标识符
            var clientIdentifier = await GetClientIdentifierAsync(context);
            
            // 检查是否在白名单中
            if (IsInWhitelist(context))
            {
                await next(context);
                return;
            }

            // 检查全局限流
            if (await IsRateLimitedAsync(clientIdentifier))
            {
                await ReturnRateLimitResponseAsync(context);
                return;
            }

            // 检查端点限流
            var endpoint = context.GetEndpoint();
            if (endpoint != null)
            {
                var rateLimitAttribute = endpoint.Metadata.GetMetadata<RateLimitAttribute>();
                if (rateLimitAttribute != null)
                {
                    if (await IsEndpointRateLimitedAsync(clientIdentifier, rateLimitAttribute))
                    {
                        await ReturnRateLimitResponseAsync(context, rateLimitAttribute);
                        return;
                    }
                }
            }

            // 记录请求
            await RecordRequestAsync(clientIdentifier, context);

            try
            {
                await next(context);
            }
            finally
            {
                // 释放限流器许可
                _limiter.Dispose();
            }
        }

        /// <summary>
        /// 获取客户端标识符
        /// </summary>
        private async Task<string> GetClientIdentifierAsync(HttpContext context)
        {
            // 优先使用用户ID
            if (context.User.Identity.IsAuthenticated)
            {
                var userId = context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
                if (!string.IsNullOrEmpty(userId))
                {
                    return $"user:{userId}";
                }
            }

            // 使用IP地址
            var ipAddress = GetClientIpAddress(context);
            
            // 如果启用API密钥，尝试获取API密钥
            if (_options.EnableApiKeyAuth)
            {
                var apiKey = GetApiKey(context);
                if (!string.IsNullOrEmpty(apiKey))
                {
                    return $"api_key:{apiKey}";
                }
            }

            return $"ip:{ipAddress}";
        }

        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        private string GetClientIpAddress(HttpContext context)
        {
            var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            var realIp = context.Request.Headers["X-Real-IP"].FirstOrDefault();
            
            if (!string.IsNullOrEmpty(forwardedFor))
            {
                return forwardedFor.Split(',')[0].Trim();
            }

            if (!string.IsNullOrEmpty(realIp))
            {
                return realIp;
            }

            return context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
        }

        /// <summary>
        /// 获取API密钥
        /// </summary>
        private string GetApiKey(HttpContext context)
        {
            // 从Header获取
            if (context.Request.Headers.TryGetValue(_options.ApiKeyHeaderName, out var apiKey))
            {
                return apiKey;
            }

            // 从查询参数获取
            return context.Request.Query[_options.ApiKeyQueryParam].FirstOrDefault();
        }

        /// <summary>
        /// 检查是否在白名单中
        /// </summary>
        private bool IsInWhitelist(HttpContext context)
        {
            var clientIp = GetClientIpAddress(context);
            
            // 检查IP白名单
            if (_options.IPWhitelist?.Contains(clientIp) == true)
            {
                return true;
            }

            // 检查路径白名单
            var path = context.Request.Path.Value;
            if (_options.PathWhitelist?.Any(pattern => 
                System.Text.RegularExpressions.Regex.IsMatch(path, pattern)) == true)
            {
                return true;
            }

            // 检查用户白名单
            if (context.User.Identity.IsAuthenticated)
            {
                var username = context.User.Identity.Name;
                if (_options.UserWhitelist?.Contains(username) == true)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 检查是否被限流
        /// </summary>
        private async Task<bool> IsRateLimitedAsync(string clientIdentifier)
        {
            var cacheKey = $"rate_limit:{clientIdentifier}:global";
            var currentCount = await _cacheService.GetLongAsync(cacheKey);
            
            if (currentCount >= _options.MaxRequests)
            {
                _logger.LogWarning("客户端被限流: {ClientIdentifier}, 当前请求数: {Count}", clientIdentifier, currentCount);
                return true;
            }

            return false;
        }

        /// <summary>
        /// 检查端点是否被限流
        /// </summary>
        private async Task<bool> IsEndpointRateLimitedAsync(string clientIdentifier, RateLimitAttribute attribute)
        {
            var endpointKey = $"{clientIdentifier}:{attribute.EndpointKey}";
            var cacheKey = $"rate_limit:{endpointKey}";
            var currentCount = await _cacheService.GetLongAsync(cacheKey);
            
            if (currentCount >= attribute.MaxRequests)
            {
                _logger.LogWarning("端点被限流: {ClientIdentifier}, 端点: {EndpointKey}, 当前请求数: {Count}", 
                    clientIdentifier, attribute.EndpointKey, currentCount);
                return true;
            }

            return false;
        }

        /// <summary>
        /// 记录请求
        /// </summary>
        private async Task RecordRequestAsync(string clientIdentifier, HttpContext context)
        {
            // 记录全局请求
            var globalKey = $"rate_limit:{clientIdentifier}:global";
            await _cacheService.IncrementAsync(globalKey);
            
            // 记录端点请求
            var endpoint = context.GetEndpoint();
            if (endpoint != null)
            {
                var rateLimitAttribute = endpoint.Metadata.GetMetadata<RateLimitAttribute>();
                if (rateLimitAttribute != null)
                {
                    var endpointKey = $"{clientIdentifier}:{rateLimitAttribute.EndpointKey}";
                    var cacheKey = $"rate_limit:{endpointKey}";
                    await _cacheService.IncrementAsync(cacheKey);
                }
            }

            // 记录请求统计
            await RecordRequestStatisticsAsync();
        }

        /// <summary>
        /// 记录请求统计
        /// </summary>
        private async Task RecordRequestStatisticsAsync()
        {
            var today = DateTime.UtcNow.ToString("yyyy-MM-dd");
            var statsKey = $"rate_limit:stats:{today}";
            
            await _cacheService.IncrementAsync(statsKey);
        }

        /// <summary>
        /// 返回限流响应
        /// </summary>
        private async Task ReturnRateLimitResponseAsync(HttpContext context, RateLimitAttribute attribute = null)
        {
            var retryAfter = attribute?.WindowMinutes ?? _options.WindowMinutes;
            
            context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
            context.Response.Headers["Retry-After"] = retryAfter.ToString();
            context.Response.Headers["X-RateLimit-Limit"] = (attribute?.MaxRequests ?? _options.MaxRequests).ToString();
            context.Response.Headers["X-RateLimit-Remaining"] = "0";
            context.Response.Headers["X-RateLimit-Reset"] = 
                DateTime.UtcNow.AddMinutes(retryAfter).ToString("yyyy-MM-ddTHH:mm:ssZ");

            var response = new RateLimitResponse
            {
                Message = "请求过于频繁，请稍后再试",
                RetryAfter = retryAfter,
                Limit = attribute?.MaxRequests ?? _options.MaxRequests,
                Window = retryAfter
            };

            await context.Response.WriteAsJsonAsync(response);
            
            _logger.LogWarning("返回限流响应: {ClientIdentifier}, 状态码: {StatusCode}", 
                await GetClientIdentifierAsync(context), context.Response.StatusCode);
        }
    }

    /// <summary>
    /// 限流响应
    /// </summary>
    public class RateLimitResponse
    {
        public string Message { get; set; }
        public int RetryAfter { get; set; }
        public int Limit { get; set; }
        public int Window { get; set; }
        public DateTime Timestamp { get; set; } = DateTime.UtcNow;
    }

    /// <summary>
    /// 缓存服务扩展方法
    /// </summary>
    public static class CacheServiceExtensions
    {
        public static async Task<long> IncrementAsync(this DistributedCacheService cacheService, string key)
        {
            var currentValue = await cacheService.GetLongAsync(key);
            var newValue = currentValue + 1;
            await cacheService.SetLongAsync(key, newValue, TimeSpan.FromMinutes(1));
            return newValue;
        }
    }
}